#region Namespaces

using System;
using System.Collections.Generic;

#endregion

namespace Vibstudio.X4NET.Xml.Linq
{
    public sealed class XNamespace
    {
        private static readonly XNamespace blank;

        private static readonly XNamespace xml;

        private static readonly XNamespace xmlns;

        private static readonly Dictionary<string, XNamespace> nstable;

        private readonly string uri;

        private Dictionary<string, XName> table;

        static XNamespace()
        {
            nstable = new Dictionary<string, XNamespace>();
            blank = Get(String.Empty);
            xml = Get("http://www.w3.org/XML/1998/namespace");
            xmlns = Get("http://www.w3.org/2000/xmlns/");
        }

        private XNamespace(string namespaceName)
        {
            if (namespaceName == null)
            {
                throw new ArgumentNullException("namespaceName");
            }
            uri = namespaceName;
        }

        public static XNamespace None
        {
            get { return blank; }
        }

        public static XNamespace Xml
        {
            get { return xml; }
        }

        public static XNamespace Xmlns
        {
            get { return xmlns; }
        }

        public string NamespaceName
        {
            get { return uri; }
        }

        public static XNamespace Get(string namespaceName)
        {
            lock (nstable)
            {
                XNamespace ret;
                if (!nstable.TryGetValue(namespaceName, out ret))
                {
                    ret = new XNamespace(namespaceName);
                    nstable[namespaceName] = ret;
                }
                return ret;
            }
        }

        public XName GetName(string localName)
        {
            lock (nstable)
            {
                if (table == null)
                {
                    table = new Dictionary<string, XName>();
                }

                XName ret;
                if (!table.TryGetValue(localName, out ret))
                {
                    ret = new XName(localName, this);
                    table[localName] = ret;
                }
                return ret;
            }
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(this, obj))
            {
                return true;
            }
            XNamespace ns = obj as XNamespace;
            return ns != null && uri == ns.uri;
        }

        public static bool operator ==(XNamespace left, XNamespace right)
        {
            return (object)left != null ? left.Equals(right) : (object)right == null;
        }

        public static bool operator !=(XNamespace left, XNamespace right)
        {
            return !(left == right);
        }

        public static XName operator +(XNamespace ns, string localName)
        {
            return new XName(localName, ns);
        }

        [CLSCompliant(false)]
        public static implicit operator XNamespace(string namespaceName)
        {
            return namespaceName != null ? Get(namespaceName) : null;
        }

        public override int GetHashCode()
        {
            return uri.GetHashCode();
        }

        public override string ToString()
        {
            return uri;
        }
    }
}